#define WIN32_LEAN_AND_MEAN
#include <python.h>
#include <Windows.h>
#include <iostream>
#include <string>
#include <vector>

// Specifies the python file to execute on run, relative to the current directory.
#define AUTOEXEC_FILENAME "app\\kharlia_init.py"
// Specifies the engine's application ID, so it knows what configuration to use
#if defined(EXEFILE_KHARLIA)
#define APP_ID "testgame"
#elif defined(EXEFILE_KHARLIA_SERVER)
#define APP_ID "kharlia_server"
#elif defined(EXEFILE_EDITOR)
#define APP_ID "editor"
#elif defined(EXEFILE_CMD)
#define APP_ID "cmd"
#endif
// Specifies what the current directory will be set to relative to the directory
// of this executable file.
#define RELATIVE_CURRENT_DIR L".."
// Specifies the directory containing the python DLL that is delay loaded.
#define DLL_DIRECTORY L"bin"
// If true, doesn't allow arbitrary commands to be executed
#define FORCE_ARGS 0
// Adds option -u for unbuffered std streams
#define UNBUFFERED_OUTPUT 1

#define PATH_SEP '\\'
#define PATH_SEP_W L'\\'

using std::cout;
using std::wcout;
using std::endl;
using std::string;
using std::wstring;
using std::vector;

static vector<string> argvcont;

static int newargc;
static char** newargv;

wstring GetExecutablePath() {
    wchar_t buffer[MAX_PATH];
    DWORD len = GetModuleFileNameW(NULL, buffer, MAX_PATH);
    return wstring(buffer);
}

string GetPathHead(const string& filename) {
    return filename.substr(0, filename.rfind(PATH_SEP) + 1);
}

wstring GetPathHead(const wstring& filename) {
    return filename.substr(0, filename.rfind(PATH_SEP_W) + 1);
}

void JoinPath(wstring& dest, const wstring& src) {
    if (dest.size() > 0 && dest[dest.size() - 1] == PATH_SEP_W)
        dest += src;
    else
        dest += PATH_SEP_W + src;
}

void PrintArgv() {
    if (argvcont.size() > 0) {
        cout << "argv: ";
        for (unsigned int i = 0; i < argvcont.size(); i ++) {
            cout << argvcont[i] << ", ";
        }
        cout << endl;
    }
}

int main(int argc, char* argv[]) {
    // Copy argc and argv
    for (int i = 0; i < argc; i ++) {
        argvcont.push_back(string(argv[i]));
    }
    int ifile = -1, // script name
        iinter = -1, // -i (interactive mode)
        inosite = -1, // -S (no-site import)
        impfork = -1, // --multiprocessing-fork
        icode = -1, // -c "script"
        iappid = -1, // --appid "appid"
        iunbuffered = -1; // -u (unbuffered output)
    // Iterate through arguments and locate what we have
    for (unsigned int i = 0; i < argvcont.size(); i++) {
        if (argvcont[i] == "-S") {
            inosite = i;
        }
        else if (argvcont[i] == "--multiprocessing-fork") {
            impfork = i;
        }
        else if (argvcont[i] == "-c") {
            icode = i;
        }
        else if (argvcont[i] == "--appid") {
            iappid = i;
        }
        else if (argvcont[i] == "-u") {
            iunbuffered = i;
        }
        else if (argvcont[i] == "-i") {
            iinter = i;
        }
        //else if (ifile == -1 &&
        //         argvcont[i][0] != '-' &&
        //         argvcont[i][0] != '+' &&
        //         (iappid == -1 || i != iappid + 1) {
        //    ifile = i;
        //}
    }
    if (!FORCE_ARGS) {
        int inpos = 1; // Insert arguments at index 1 by default
        if (ifile != -1)
            inpos = ifile + 1; // Insert after positional argument
        // Only make changes if this isn't a multiprocessing fork
        if (impfork == -1 && iinter == -1) {
            // Now lets insert arguments if they're not already specified on the command line
            if (icode == -1 && ifile == -1 && iappid == -1) {
                argvcont.insert(argvcont.begin() + inpos, APP_ID);
                argvcont.insert(argvcont.begin() + inpos, "--appid");
                argvcont.insert(argvcont.begin() + inpos, AUTOEXEC_FILENAME);
            }
            if (inosite == -1) {
                argvcont.insert(argvcont.begin() + inpos, "-S");
            }
            if (iunbuffered == -1 && UNBUFFERED_OUTPUT) {
                argvcont.insert(argvcont.begin() + inpos, "-u");
            }
        }
    }
    else {
        argvcont.insert(argvcont.begin() + 1, APP_ID);
        argvcont.insert(argvcont.begin() + 1, "--appid");
        argvcont.insert(argvcont.begin() + 1, AUTOEXEC_FILENAME);
        argvcont.insert(argvcont.begin() + 1, "-S");
        if (UNBUFFERED_OUTPUT)
            argvcont.insert(argvcont.begin() + 1, "-u");
    }
    //PrintArgv();
    // Create newargc and newargv
    newargc = argvcont.size();
    newargv = new char*[newargc];
    for (int i = 0; i < newargc; i ++) {
        newargv[i] = new char[argvcont[i].length() + 1];
        strcpy(newargv[i], argvcont[i].c_str());
    }
    // Set current directory to the directory of this file
    wstring exepath = GetExecutablePath();
    wstring exefolder = GetPathHead(exepath);
    JoinPath(exefolder, RELATIVE_CURRENT_DIR);
    SetCurrentDirectoryW(exefolder.c_str());
    SetDllDirectoryW(DLL_DIRECTORY);
    // Execute Python Main
    switch (Py_Main(newargc, newargv)) {
        case 0:
            break;
        case 1:
            cout << "ExeFile: exited with exception" << endl;
            break;
        case 2:
            cout << "ExeFile: Invalid parameter list" << endl;
            break;
        default:
            cout << "ExeFile: Invalid exit value" << endl;
            break;
    }
    // Delete argument list copies
    for (int i = 0; i < newargc; i ++) {
        delete[] newargv[i];
    }
    delete[] newargv;
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow) {
    return main(__argc, __argv);
}